/*
    1. 抽象日志器类
    2. 派生出不同的子类（同步日志器 &  异步日志器)
*/

#pragma once
#define _GUN_SOURCE
#include "util.hpp"
#include "level.hpp"
#include "format.hpp"
#include "sink.hpp"
#include <atomic>
#include <cstdarg>
#include <mutex>
#include <cassert>
#include "looper.hpp"
#include "unordered_map"

namespace log
{
    class Logger
    {
    public:
        using ptr = std::shared_ptr<Logger>;
        // 完成构造日志消息的过程并进行格式化，达到格式化之后日志消息字符串，然后输出

        Logger(const std::string &logger_name, LogLevel::value level, Formatter::ptr &formatter, std::vector<LogSink::ptr> &sinks)
            : _logger_name(logger_name), _limit_level(level), _formatter(formatter), _sinks(sinks)
        {
        }

        std::string getName()
        {
            return _logger_name;
        }

        void debug(const std::string &file, size_t line, const std::string &fmt, ...)
        {
            // 1. 判断是否可以输出
            if (LogLevel::value::DEBUG < _limit_level)
            {
                return;
            }

            // 2. 对fmt格式化字符串和不定参字符串组织，得到的日志消息字符串

            va_list ap;
            va_start(ap, fmt);
            char *res;
            int ret = vasprintf(&res, fmt.c_str(), ap);
            if (ret == -1)
            {
                std::cout << "vasprintf failed\n";
                return;
            }

            va_end(ap);
            serialize(LogLevel::value::DEBUG, file, line, res);
            // 注意这里要释放，因为vasprintf是开辟空间给了res
            free(res);
        }

        void info(const std::string &file, size_t line, const std::string &fmt, ...)
        {
            if (LogLevel::value::INFO < _limit_level)
            {
                return;
            }

            va_list ap;
            va_start(ap, fmt);
            char *res;
            int ret = vasprintf(&res, fmt.c_str(), ap);
            if (ret == -1)
            {
                std::cout << "vasprintf failed\n";
                return;
            }

            va_end(ap);
            serialize(LogLevel::value::INFO, file, line, res);
            // 注意这里要释放，因为vasprintf是开辟空间给了res
            free(res);
        }

        void warn(const std::string &file, size_t line, const std::string &fmt, ...)
        {
            if (LogLevel::value::WARN < _limit_level)
            {
                return;
            }

            va_list ap;
            va_start(ap, fmt);
            char *res;
            int ret = vasprintf(&res, fmt.c_str(), ap);
            if (ret == -1)
            {
                std::cout << "vasprintf failed\n";
                return;
            }

            va_end(ap);
            serialize(LogLevel::value::WARN, file, line, res);
            // 注意这里要释放，因为vasprintf是开辟空间给了res
            free(res);
        }

        void error(const std::string &file, size_t line, const std::string &fmt, ...)
        {
            if (LogLevel::value::ERROR < _limit_level)
            {
                return;
            }

            va_list ap;
            va_start(ap, fmt);
            char *res;
            int ret = vasprintf(&res, fmt.c_str(), ap);
            if (ret == -1)
            {
                std::cout << "vasprintf failed\n";
                return;
            }

            va_end(ap);
            serialize(LogLevel::value::ERROR, file, line, res);
            // 注意这里要释放，因为vasprintf是开辟空间给了res
            free(res);
        }

        void fatal(const std::string &file, size_t line, const std::string &fmt, ...)
        {
            if (LogLevel::value::FATAL < _limit_level)
            {
                return;
            }

            va_list ap;
            va_start(ap, fmt);
            char *res;
            int ret = vasprintf(&res, fmt.c_str(), ap);
            if (ret == -1)
            {
                std::cout << "vasprintf failed\n";
                return;
            }

            va_end(ap);
            serialize(LogLevel::value::FATAL, file, line, res);
            // 注意这里要释放，因为vasprintf是开辟空间给了res
            free(res);
        }

    protected:
        void serialize(LogLevel::value level, const std::string &file, size_t line, char *str)
        {
            // 构造msg
            LogMessage msg(level, line, file, _logger_name, str);
            // 4. 通过格式化工具对LogMsg进行格式化，得到格式化的日志消息
            std::stringstream ss;
            _formatter->format(ss, msg);
            // 根据不同的落地模式 向不同的方式里面打日志
            log(ss.str().c_str(), ss.str().size());
        }

    private:
        // 抽象接口完成实际的输出
        virtual void log(const char *data, size_t len) = 0;

    protected:
        std::mutex _mutex;
        std::string _logger_name;
        std::atomic<LogLevel::value> _limit_level;
        Formatter::ptr _formatter;
        std::vector<LogSink::ptr> _sinks;
    };

    // 同步日志器
    class SyncLogger : public Logger
    {
    public:
        SyncLogger(const std::string &logger_name, LogLevel::value level, Formatter::ptr &formatter, std::vector<LogSink::ptr> sinks)
            : Logger(logger_name, level, formatter, sinks)
        {
        }

    protected:
        void log(const char *data, size_t len) override
        {
            std::unique_lock<std::mutex> lock(_mutex);
            if (_sinks.empty())
            {
                return;
            }

            // 根据不同类型实现落地
            for (auto &sink : _sinks)
            {
                sink->log(data, len);
            }
        }

    private:
    };

    // 异步日志器
    class AsyncLogger : public Logger
    {
    public:
        // 用了bind ，因为回调函数只有一个参数，而realog里面还有一个this指针，所以直接bind成一个整体传过去
        AsyncLogger(const std::string &logger_name, LogLevel::value level, Formatter::ptr &formatter, std::vector<LogSink::ptr> sinks, Asynctype looper_type)
            : Logger(logger_name, level, formatter, sinks),
              _looper(std::make_shared<AsyncLooper>(std::bind(&AsyncLogger::realLog, this, std::placeholders::_1), looper_type))
        {
        }

        // 将数据写入缓冲区不再直接写到内存中
        void log(const char *data, size_t len) override
        {
            _looper->push(data, len);
        }

        void realLog(Buffer &buf)
        {
            if (_sinks.empty())
            {
                return;
            }

            for (auto &sink : _sinks)
            {
                sink->log(buf.begin(), buf.readAbleSize());
            }
        }

    private:
        AsyncLooper::ptr _looper;
    };

    // 使用建造者模式来创建日志器，而不是让用户来自己构造，简化了创建对象的复杂性
    //  1. 抽象出一个日志器建造者类
    //  2. 派生出具体的建造者类

    enum class LoggerType
    {
        LOGGER_SYNC,
        LOGGER_ASYNC
    };

    class LoggerBuilder
    {
    public:
        LoggerBuilder()
            : _logger_type(LoggerType::LOGGER_SYNC),
              _limit_level(LogLevel::value::DEBUG),
              _looper_type(Asynctype::ASYNC_SAFE)
        {
        }

        void buildLoggerType(LoggerType type)
        {
            _logger_type = type;
        }

        void buildLoggerName(const std::string &name)
        {
            _logger_name = name;
        }

        void buildLoggerLevel(LogLevel::value &level)
        {
            _limit_level = level;
        }

        void buildLoggerFormatter(const std::string &pattern = "[%d{%H:%M:%S}][%t][%c][%f:%l][%p]%T%m%n")
        {
            _formatter = std::make_shared<Formatter>(pattern);
        }

        void buildEnableUnSafeAsync()
        {
            _looper_type = Asynctype::ASUNC_UNSAFE;
        }

        template <typename SinkType, typename... Args>
        void buildSink(Args &&...args)
        {
            LogSink::ptr psink = SinkFactory::create<SinkType>(std::forward<Args>(args)...);
            _sinks.emplace_back(psink);
        }

        virtual Logger::ptr build() = 0;

    protected:
        Asynctype _looper_type;
        LoggerType _logger_type;
        std::string _logger_name;
        LogLevel::value _limit_level;
        Formatter::ptr _formatter;
        std::vector<LogSink::ptr> _sinks;
    };

    //
    class LocalLoggerBuilder : public LoggerBuilder
    {
    public:
        LocalLoggerBuilder()
        {
        }

        Logger::ptr build() override
        {
            assert(!_logger_name.empty());
            if (_formatter.get() == nullptr)
            {
                _formatter = std::make_shared<Formatter>();
            }

            if (_sinks.empty())
            {
                buildSink<StdoutSink>();
            }

            if (_logger_type == LoggerType::LOGGER_ASYNC)
            {
                return std::make_shared<AsyncLogger>(_logger_name, _limit_level, _formatter, _sinks, _looper_type);
            }

            return std::make_shared<SyncLogger>(_logger_name, _limit_level, _formatter, _sinks);
        }
    };

    class LoggerManager
    {
    public:
        // 懒汉模式
        static LoggerManager &GetInstance()
        {
            // C++11之后的写法，线程安全的， 当其他线程来的时候，如果没有创建对象就会阻塞住
            static LoggerManager eton;
            return eton;
        }

        void addLogger(Logger::ptr logger)
        {
            if (HasLogger(logger->getName()))
                return;
            std::unique_lock<std::mutex> lock(_mutex);
            _loggers.insert(std::make_pair(logger->getName(), logger));
        }

        bool HasLogger(const std::string &name)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _loggers.find(name);
            if (it == _loggers.end())
            {
                return false;
            }

            return true;
        }

        Logger::ptr getLogger(const std::string &name)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _loggers.find(name);
            if (it == _loggers.end())
            {
                return Logger::ptr();
            }

            return it->second;
        }

        Logger::ptr rootLogger()
        {
            return _root_logger;
        }

    private:
        LoggerManager()
        {
            std::unique_ptr<log::LoggerBuilder> builder(new LocalLoggerBuilder());
            builder->buildLoggerName("root");

            _root_logger = builder->build();
            _loggers.insert(std::make_pair("root", _root_logger));
        }

    private:
        std::mutex _mutex;
        Logger::ptr _root_logger; // 默认日志器
        std::unordered_map<std::string, Logger::ptr> _loggers;
    };

    // 全局的日志器建筑者，用户不再关心单例里面发生了什么，直接调用全局的建筑者直接完成对象的创建
    class GlobalLoggerBuilder : public LoggerBuilder
    {
    public:
        using ptr = std::shared_ptr<GlobalLoggerBuilder>;
        GlobalLoggerBuilder()
        {
        }

        Logger::ptr build() override
        {
            assert(!_logger_name.empty());
            if (_formatter.get() == nullptr)
            {
                _formatter = std::make_shared<Formatter>();
            }

            if (_sinks.empty())
            {
                buildSink<StdoutSink>();
            }

            Logger::ptr logger;
            if (_logger_type == LoggerType::LOGGER_ASYNC)
            {
                logger = std::make_shared<AsyncLogger>(_logger_name, _limit_level, _formatter, _sinks, _looper_type);
            }
            else
            {
                logger = std::make_shared<SyncLogger>(_logger_name, _limit_level, _formatter, _sinks);
            }

            LoggerManager::GetInstance().addLogger(logger);

            return logger;
        }
    };
}
